use actix_web::{web, HttpResponse, Responder};
use rusqlite::{params, Connection};
use serde::{Deserialize, Serialize};
use std::path::Path;

use crate::{log_error, log_info};

const DB_PATH: &str = "db/rsts.db";

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SshServer {
    pub id: i64,
    pub alias: String,
    pub hostname: String,
    pub port: i32,
    pub username: String,
    pub password: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct SshServerInput {
    pub alias: String,
    pub hostname: String,
    pub port: Option<i32>,
    pub username: String,
    pub password: Option<String>,
}

fn ensure_table(conn: &Connection) -> rusqlite::Result<()> {
    conn.execute(
        "CREATE TABLE IF NOT EXISTS ssh_servers (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            alias TEXT NOT NULL,
            hostname TEXT NOT NULL,
            port INTEGER NOT NULL DEFAULT 22,
            username TEXT NOT NULL,
            password TEXT
        )",
        [],
    )?;
    Ok(())
}

fn open_db() -> rusqlite::Result<Connection> {
    if !Path::new(DB_PATH).exists() {
        // 如果数据库不存在，创建空文件
        std::fs::create_dir_all("db").ok();
        std::fs::File::create(DB_PATH).ok();
    }
    let conn = Connection::open(DB_PATH)?;
    ensure_table(&conn)?;
    Ok(conn)
}

pub async fn list_servers() -> impl Responder {
    match open_db() {
        Ok(conn) => {
            let mut stmt = match conn.prepare(
                "SELECT id, alias, hostname, port, username, password FROM ssh_servers ORDER BY alias ASC, id ASC",
            ) {
                Ok(s) => s,
                Err(e) => {
                    log_error!("prepare failed: {}", e);
                    return HttpResponse::InternalServerError().body(format!("DB error: {}", e));
                }
            };
            let rows = stmt.query_map([], |row| {
                Ok(SshServer {
                    id: row.get(0)?,
                    alias: row.get(1)?,
                    hostname: row.get(2)?,
                    port: row.get(3)?,
                    username: row.get(4)?,
                    password: row.get(5).ok(),
                })
            });
            match rows {
                Ok(iter) => {
                    let mut servers: Vec<SshServer> = Vec::new();
                    for item in iter {
                        if let Ok(s) = item {
                            servers.push(s);
                        }
                    }
                    HttpResponse::Ok().json(servers)
                }
                Err(e) => {
                    log_error!("query_map failed: {}", e);
                    HttpResponse::InternalServerError().body(format!("DB error: {}", e))
                }
            }
        }
        Err(e) => HttpResponse::InternalServerError().body(format!("DB open error: {}", e)),
    }
}

pub async fn create_server(payload: web::Json<SshServerInput>) -> impl Responder {
    match open_db() {
        Ok(conn) => {
            let port = payload.port.unwrap_or(22);
            match conn.execute(
                "INSERT INTO ssh_servers (alias, hostname, port, username, password) VALUES (?1, ?2, ?3, ?4, ?5)",
                params![payload.alias, payload.hostname, port, payload.username, payload.password.clone()],
            ) {
                Ok(_) => {
                    let id = conn.last_insert_rowid();
                    let server = SshServer {
                        id,
                        alias: payload.alias.clone(),
                        hostname: payload.hostname.clone(),
                        port,
                        username: payload.username.clone(),
                        password: payload.password.clone(),
                    };
                    log_info!("Created ssh server config: {}@{}:{} ({})", server.username, server.hostname, server.port, server.alias);
                    HttpResponse::Ok().json(server)
                }
                Err(e) => HttpResponse::InternalServerError().body(format!("DB insert error: {}", e)),
            }
        }
        Err(e) => HttpResponse::InternalServerError().body(format!("DB open error: {}", e)),
    }
}

pub async fn update_server(path: web::Path<i64>, payload: web::Json<SshServerInput>) -> impl Responder {
    let id = path.into_inner();
    match open_db() {
        Ok(conn) => {
            let port = payload.port.unwrap_or(22);
            match conn.execute(
                "UPDATE ssh_servers SET alias=?1, hostname=?2, port=?3, username=?4, password=?5 WHERE id=?6",
                params![payload.alias, payload.hostname, port, payload.username, payload.password.clone(), id],
            ) {
                Ok(rows) => {
                    if rows == 0 {
                        return HttpResponse::NotFound().body("Not found");
                    }
                    let server = SshServer {
                        id,
                        alias: payload.alias.clone(),
                        hostname: payload.hostname.clone(),
                        port,
                        username: payload.username.clone(),
                        password: payload.password.clone(),
                    };
                    HttpResponse::Ok().json(server)
                }
                Err(e) => HttpResponse::InternalServerError().body(format!("DB update error: {}", e)),
            }
        }
        Err(e) => HttpResponse::InternalServerError().body(format!("DB open error: {}", e)),
    }
}

pub async fn delete_server(path: web::Path<i64>) -> impl Responder {
    let id = path.into_inner();
    match open_db() {
        Ok(conn) => match conn.execute("DELETE FROM ssh_servers WHERE id=?1", params![id]) {
            Ok(rows) => {
                if rows == 0 {
                    HttpResponse::NotFound().body("Not found")
                } else {
                    HttpResponse::Ok().body("OK")
                }
            }
            Err(e) => HttpResponse::InternalServerError().body(format!("DB delete error: {}", e)),
        },
        Err(e) => HttpResponse::InternalServerError().body(format!("DB open error: {}", e)),
    }
}